﻿using System;
using System.Linq;

namespace NeuralLib.Learning
{
    public class KohonenSuccessiveLearning : KohonenLearning
    {     
        public KohonenSuccessiveLearning(KohonenNetwork net, int iterationsCount)
        {
            _network = net;
            _iterationsCount = iterationsCount;
            _initialEfficientWidth = _efficientWidth = Math.Min(net.KmLayer.NeuronsXCount, net.KmLayer.NeuronsYCount) / 2;
            _efficientWidthScaleSpeed = iterationsCount / _initialEfficientWidth;
            _initialLearningRate = _learningRate = 0.1;
            _learningRateScaleSpeed = 1000;
        }

        public override void Run(double[][] input)
        {
            var samplesCount = input.Count();
            for (var i = 0; i < _iterationsCount; i++)
            {
                for (var j = 0; j < samplesCount; j++)
                {
                    var winnerInd = _network.KmLayer.Winner(input[j]);
                    var winner = _network.KmLayer.Neurons[winnerInd];
                    WeightsUpdate(winner, input[j]);
                    
                }
                UpdateEfficientWidth(i + 1);
                UpdateLearningRate(i + 1);
                if (NetworkEpochCompleted != null)
                    NetworkEpochCompleted(null, new EventArgs());
            }
            var error = CalculateError(input);
            var deadNeurons = DeadNeurons(input);

            if (NetworkLearnCompleted != null)
                NetworkLearnCompleted(null, new LearnerEventArgs(error, deadNeurons));
        }       

        private void WeightsUpdate(KohonenNeuron winner, double[] input)
        {
            var neuronsCount = _network.KmLayer.NeuronsCount;
            var weightsCount = _network.KmLayer.Neurons[0].Weights.Count();

            for (var i = 0; i < neuronsCount; i++)
            {
                var neuron = _network.KmLayer.Neurons[i];

                for (var j = 0; j < weightsCount; j++)

                    neuron.Weights[j] += _learningRate * NeuronsDistance(winner, neuron) * (input[j] - neuron.Weights[j]);
            }
        }

        public event EventHandler NetworkEpochCompleted;
        public event EventHandler NetworkLearnCompleted;

    }
}